package ru.dc.gui.swing;

import org.apache.commons.lang.StringUtils;
import org.jdom.JDOMException;
import org.noos.xing.mydoggy.ContentManager;
import org.noos.xing.mydoggy.ToolWindow;
import org.noos.xing.mydoggy.ToolWindowAnchor;
import org.noos.xing.mydoggy.ToolWindowManagerDescriptor;
import org.noos.xing.mydoggy.plaf.MyDoggyToolWindowManager;
import ru.dc.*;
import ru.dc.gui.swing.settings.SettingsForm;
import ru.dc.gui.swing.util.ClosableTabComponent;
import ru.dc.object.*;
import ru.dc.object.events.download.DownloadQueueEvent;
import ru.dc.object.events.hub.HubEvent;
import ru.dc.object.events.hub.PrivateMessageRecieved;
import ru.dc.object.events.manager.AddConnection;
import ru.dc.object.events.manager.ConnectionManagerEvent;
import ru.dc.util.FilelistUtils;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: deady
 * Date: 08.10.2010
 * Time: 23:54:13
 */
public class MainFrame extends JFrame {

    private JTabbedPane tabs;
    private Map<ServerInfo, HubConnectionController> controllerMap = new HashMap<ServerInfo, HubConnectionController>();
    private MyDoggyToolWindowManager toolWindowManager;

    public MainFrame() throws HeadlessException {


        setTitle("YADCC");
        setSize(800, 600);
        setLocation((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() -getWidth())/2,
                (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() -getHeight())/3);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        tabs = new JTabbedPane();
        //getContentPane().add(tabs);


        toolWindowManager = new MyDoggyToolWindowManager();
        //отключаем обработку шорткатов alt+1 итд, чтобы исключить конфликт с такой же комбинацией для
        //переключения вкладок
        toolWindowManager.getToolWindowManagerDescriptor().setNumberingEnabled(false);
        ContentManager contentManager = toolWindowManager.getContentManager();
        contentManager.addContent("Content", "Content", null, tabs);
        JPanel rootPanel = new JPanel();
        rootPanel.setLayout(new BorderLayout());
        rootPanel.add(toolWindowManager, BorderLayout.CENTER);
        getContentPane().add(rootPanel);

        addToolWindow("downloads", "Загрузки", null, new DownloadsPanel(), ToolWindowAnchor.BOTTOM);

        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);
        JMenu file = new JMenu("Файл");
        menuBar.add(file);

        JMenuItem connect = new JMenuItem("Соединиться с хабом");
        file.add(connect);
        connect.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doConnect();
            }
        });

        JMenuItem search = new JMenuItem("Найти");
        file.add(search);
        search.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doSearch();
            }
        });

        JMenuItem openFileList = new JMenuItem("Открыть список файлов");
        file.add(openFileList);
        openFileList.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser c = new JFileChooser();
                if (c.showOpenDialog(MainFrame.this)== JFileChooser.APPROVE_OPTION) {
                    FilesList fl = new FilesList();
                    try {
                        fl.setFileList(FilelistUtils.getFiles(c.getSelectedFile()));
                        addTab("Список файлов: "+c.getSelectedFile(), fl);
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    } catch (JDOMException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });

        file.addSeparator();

        JMenuItem settings = new JMenuItem("Настройки");
        settings.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SettingsForm f = new SettingsForm(MainFrame.this);
                f.setVisible(true);
            }
        });
        file.add(settings);

        JMenuItem exit = new JMenuItem("Выход");
        exit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Application.getApplication().close();
            }
        });
        file.add(exit);

        final JMenu window = new JMenu("Окна");
        menuBar.add(window);
        JMenuItem listConnections = new JMenuItem("Список соединений");
        listConnections.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doListConnections();
            }
        });
        window.add(listConnections);

        JMenuItem publicHubs = new JMenuItem("Публичные хабы");
        window.add(publicHubs);
        AbstractAction showPublicHubs = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                doListPublicHubs();
            }
        };
        publicHubs.addActionListener(showPublicHubs);

        JMenuItem favouritesHubs = new JMenuItem("Избранные хабы");
        window.add(favouritesHubs);
        AbstractAction showFavouritesHubs = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                doListFavouriteHubs();
            }
        };
        favouritesHubs.addActionListener(showFavouritesHubs);

        JMenuItem searchSpy = new JMenuItem("Поисковый шпион");
        window.add(searchSpy);
        AbstractAction showSearchSpy = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                doShowSearchSpy();
            }
        };
        searchSpy.addActionListener(showSearchSpy);



        rootPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK), "connect");
        rootPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), "publicHubs");
        rootPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK), "search");

        rootPanel.getActionMap().put("search", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                doSearch();
            }
        });
        rootPanel.getActionMap().put("publicHubs", showPublicHubs);
        rootPanel.getActionMap().put("connect", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                doConnect();
            }
        });







        Application.getApplication().getHubEventsTranslator().addEventListener(new HubEventListener() {
            public void onEvent(HubEvent e) {

                if (!(e instanceof PrivateMessageRecieved)) {
                    return;
                }

                final PrivateMessageRecieved m = (PrivateMessageRecieved) e;
                
                //если вкладка для данного чата уже есть, то ничего не делаем - вкладка также получит соответствующее событие
                final String remoteNick =  m.getSourceNick();
                final ServerInfo hubInfo = m.getServerInfo();

                final PrivateChat privateChat = findPrivateChat(remoteNick, hubInfo);
                if (privateChat !=null) {
                    privateChat.alert();
                    return;
                }


                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        PrivateChat chat = openPrivateChatWindow(remoteNick, hubInfo);
                        chat.appendMessage(m.getSenderNick(), m.getMessage());
                    }
                });

            }
        });


        Application.getApplication().getConnectionManager().addEventListener(new ConnectionManagerEventListener() {
            public void onEvent(ConnectionManagerEvent event) {
                if (event instanceof AddConnection) {
                    AddConnection addConnection = (AddConnection) event;
                    if (((AddConnection) event).isHubConnection()) {
                        final ServerInfo serverInfo = addConnection.getServerInfo();
                        addHubConnectionTab(serverInfo);
                    }

                }
            }
        });


        Application.getApplication().getDownloadQueue().addDownloadListener(new DownloadListener() {
            public void onDownloadEvent(final DownloadQueueEvent e) {
                if (e.getOperation()== DownloadQueueEvent.ITEM_DOWNLOADED && FilelistUtils.isFilelist(e.getItem())) {
                    File file = new File(FilelistUtils.getFilelistsDir(),
                            FilelistUtils.generateIndividualFilelistName(e.getItem().getSource()));
                    try {
                        FileInfo f = FilelistUtils.getFiles(file);

                        final FilesList fl = new FilesList();
                        fl.setFileList(f);
                        fl.setSource(e.getItem().getSource());
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                addTab("Список файлов: "+e.getItem().getSource().getNick(), fl);
                            }
                        });

                    } catch (IOException e1) {
                        e1.printStackTrace();
                    } catch (JDOMException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });

        InputMap map = rootPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_1, KeyEvent.ALT_DOWN_MASK), "showTab1");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.ALT_DOWN_MASK), "showTab2");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_3, KeyEvent.ALT_DOWN_MASK), "showTab3");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_4, KeyEvent.ALT_DOWN_MASK), "showTab4");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_5, KeyEvent.ALT_DOWN_MASK), "showTab5");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_6, KeyEvent.ALT_DOWN_MASK), "showTab6");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_7, KeyEvent.ALT_DOWN_MASK), "showTab7");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_8, KeyEvent.ALT_DOWN_MASK), "showTab8");
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_9, KeyEvent.ALT_DOWN_MASK), "showTab9");

        for (int i = 1; i<=9; i++) {
            final int tabIndex = i;
            rootPanel.getActionMap().put("showTab"+i, new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    if (tabIndex<=tabs.getTabCount()) {
                        selectTab(tabIndex-1);
                    }
                }
            });
        }

        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK), "closeActiveTab");
        tabs.getActionMap().put("closeActiveTab", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                closeTab((Tab) tabs.getSelectedComponent());
            }
        });


        tabs.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                final Tab tab = (Tab) tabs.getSelectedComponent();
                if (tab!=null) {
                    tab.onSelect();
                }
            }
        });

    }

    public PrivateChat openPrivateChatWindow(String remoteNick, ServerInfo hubInfo) {
        PrivateChat chat = new PrivateChat();
        addTab(remoteNick, chat);
        chat.setHubInfo(hubInfo);
        chat.setRemoteNick(remoteNick);
        return chat;
    }

    public ToolWindow addToolWindow(String key, String label, Icon icon, JComponent content, ToolWindowAnchor anchor) {
        ToolWindow window = toolWindowManager.registerToolWindow(key, label, icon, content, anchor);
        window.setAvailable(true);
        return window;
    }



    private PrivateChat findPrivateChat(String remoteNick, ServerInfo hubInfo) {
        for (int i = 0; i < tabs.getTabCount(); i++) {
            Tab tab = (Tab) tabs.getComponentAt(i);
            if (tab instanceof PrivateChat) {
                PrivateChat cur = (PrivateChat) tab;
                if (cur.getHubInfo().equals(hubInfo) && cur.getRemoteNick().equals(remoteNick)) {
                    return cur;
                }
            }
        }
        return null;
    }

    private void selectTab(final int tabIndex) {
        tabs.setSelectedIndex(tabIndex);
    }

    public void addHubConnectionTab(ServerInfo serverInfo) {
        if (containsTab(serverInfo)) {
            selectTab(serverInfo);
        }
        HubConnectionController ctrl = new HubConnectionController(serverInfo);
        controllerMap.put(serverInfo, ctrl);
        addTab(serverInfo.getAddress(), ctrl.getContent());
        ctrl.init();
    }

    /**
     * Добавляет вкладку "Список соединений". Если такая вкладка уже есть, открывает ее (делает текущей).
     */
    private void doListConnections() {
        int i = getTabIndex(ConnectionList.class);
        if (i>=0) {
            selectTab(i);
        }
        else {
            ConnectionList list = new ConnectionList();
            addTab("Соединения", list);
        }
    }

    /**
     * Добавляет вкладку "Публичные хабы". Если такая вкладка уже есть, открывает ее (делает текущей).
     */
    private void doListPublicHubs() {
        int i = getTabIndex(PublicHubs.class);
        if (i>=0) {
            selectTab(i);
        }
        else {
            PublicHubs list = new PublicHubs();
            addTab("Публичные хабы", list);
        }
    }

    /**
     * Добавляет вкладку "Избранные хабы". Если такая вкладка уже есть, открывает ее (делает текущей).
     */
    private void doListFavouriteHubs() {
        int i = getTabIndex(FavouriteHubs.class);
        if (i>=0) {
            selectTab(i);
        }
        else {
            FavouriteHubs list = new FavouriteHubs();
            addTab("Избранные хабы", list);
        }
    }

    /**
     * Добавляет вкладку "Поисковый шпион". Если такая вкладка уже есть, открывает ее (делает текущей).
     */
    private void doShowSearchSpy() {
        int i = getTabIndex(SearchSpy.class);
        if (i>=0) {
            selectTab(i);
        }
        else {
            SearchSpy list = new SearchSpy();
            addTab("Поисковый шпион", list);
        }
    }

    private void doConnect() {
        ConnectionParamsDlg dlg = new ConnectionParamsDlg();
        if (dlg.execute()== JOptionPane.OK_OPTION) {
            ServerInfo serverInfo = dlg.getServerInfo();
            Application.getApplication().getConnectionManager().addHubConnection(serverInfo);
        }
    }

    private void doSearch() {
        SearchWindow sw = new SearchWindow();
        addTab("Поиск", sw);
    }

    /**
     * Добавляет панель поиска и запускает поиск в ней.
     * @param request поисковый запрос для поиска
     */
    public void search(SearchRequest request) {
        SearchWindow sw = new SearchWindow();
        addTab("Поиск", sw);
        sw.search(request);
    }


    private void addTab(String title, final Tab tab) {
        ClosableTabComponent closableTabComponent = new ClosableTabComponent(tabs, tab);
        tab.setTabComponent(closableTabComponent);
        tab.setTabbedPane(tabs);
        tab.init();
        tabs.addTab(title, tab);
        int index = tabs.indexOfComponent(tab);
        closableTabComponent.setTitle(!StringUtils.isEmpty(tab.getTitle()) ? tab.getTitle() : title);
        closableTabComponent.setIcon(tab.getIcon());
        closableTabComponent.getCloseBtn().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                closeTab(tab);
            }
        });
        tabs.setTabComponentAt(index, closableTabComponent);
        selectTab(index);
    }


    public void closeTab(Tab tab) {
        tab.close();
        tabs.remove(tab);
    }

    public void selectTab(ServerInfo serverInfo) {
        HubConnectionController connectionController = findServerInfoController(serverInfo);
        Tab tab = connectionController.getContent();
        if (containsTab(serverInfo)) {
            tabs.setSelectedComponent(tab);
            tab.onSelect();
        }
    }

    public int getTabIndex(Class<? extends Tab> tabClass) {
        for (int i=0; i<tabs.getTabCount(); i++) {
            Tab tab = (Tab) tabs.getComponentAt(i);
            if (tabClass.isAssignableFrom(tab.getClass())) {
                return i;
            }
        }
        return -1;
    }

    public boolean containsTab(ServerInfo serverInfo) {

        HubConnectionController connectionController = findServerInfoController(serverInfo);
        if (connectionController==null) {
            return false;
        }
        Tab tab = connectionController.getContent();
        int index = tabs.indexOfComponent(tab);
        return index>=0;
    }

    private HubConnectionController findServerInfoController(ServerInfo serverInfo) {
        for (ServerInfo si : controllerMap.keySet()) {
            if (si.getAddress().equals(serverInfo.getAddress()) && si.getPort()==serverInfo.getPort()) {
                return controllerMap.get(si);
            }
        }
        return null;
    }

    /**
     * Помечает окно поиска активной. Все остальные окна поиска помечаются неактивными.
     * Неактивное окно поиска должно игнорировать получение результатов поиска - результаты
     * должны обрабатываться только активнмы окном.
     * @param searchWindow окно поиска
     */
    public void markSearchTabActive(SearchWindow searchWindow) {
        for (int i = 0; i < tabs.getTabCount(); i++) {
            Tab tab = (Tab) tabs.getComponentAt(i);
            if (tab instanceof SearchWindow) {
                SearchWindow w = (SearchWindow) tab;
                w.setActive(tab.equals(searchWindow));
            }
        }
    }

}
